/*
 * Copyright 2009 Max Kugland
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package org.splink.deepsplink.page.request {
	import org.splink.deepsplink.page.IPageInternal;
	import org.splink.deepsplink.page.operation.IPageOperation;
	import org.splink.deepsplink.page.operation.PageFinalizeOperation;
	import org.splink.deepsplink.page.operation.PageHideOperation;
	import org.splink.deepsplink.page.operation.PageInitializeOperation;
	import org.splink.deepsplink.page.operation.PageParamsOperation;
	import org.splink.deepsplink.page.operation.PageShowOperation;
	import org.splink.deepsplink.page.state.PageState;
	import org.splink.library.queue.IQ;
	import org.splink.library.queue.IQable;
	import org.splink.library.queue.ParallelQ;
	import org.splink.library.queue.Q;
	import org.splink.library.tree.INode;
	import org.splink.library.utils.ArrayUtils;

	import flash.display.Sprite;

	/**
	 * <code>IRequestBuilder</code> helper class which builds the different operations
	 * which an <code>IRequestBuilder</code> adds into an ordered <code>Array</code>
	 * of <code>IPageOperation</code> instances.
	 * 
	 * @author Max Kugland
	 */
	final internal class OperationBuilder {
		private var _overlayIds : Array;

		/**
		 * @param overlayIds an <code>Array</code> of Strings defining the pages to be handled as overlays
		 */
		public function OperationBuilder(overlayIds : Array) {
			_overlayIds = overlayIds;
		}

		private function isOverlay(node : INode) : Boolean {
			return ArrayUtils.contains(_overlayIds, node.id);
		}
		
		/**
		 * Adds the neccessary initialization operations for the current request.
		 * 
		 * @param display the display to which the pages are added
		 * @param ar the <code>Array</code> to which the operations are added
		 * @param doOpen an <code>Array</code> which contains the <code>INode</code>s which hold the pages to be opened
		 * @param all an <code>Array</code> which contains all <code>INode</code>s which hold the pages
		 * @param pageParams an <code>Array</code> which contains <code>PageParameter</code> instances  
		 */
		public function addInitializeOperations(display : Sprite, operations : Array, doOpen : Array, all : Array, pageParams : Array) : void {
			for each (var node : INode in all) {
				if(node.data is IPageInternal) {
					addInitializeOperation(display, operations, doOpen, node, IPageInternal(node.data), pageParams);
				}
			}
		}
		
		/**
		 * Adds the neccessary showing operations for the current request.
		 * 
		 * @param display the display to which the pages are added
		 * @param ar the <code>Array</code> to which the operations are added
		 * @param doOpen an <code>Array</code> which contains the <code>INode</code>s which hold the pages to be opened
		 * @param all an <code>Array</code> which contains all <code>INode</code>s which hold the pages
		 */
		public function addShowOperations(display : Sprite, operations : Array, doOpen : Array, all : Array) : void {
			for each (var node : INode in all) {
				if(node.data is IPageInternal) {
					addShowOperation(display, operations, doOpen, node, IPageInternal(node.data));
				}
			}
		}
		
		/**
		 * Adds the neccessary initialization and showing operations for the current request.
		 * 
		 * @param display the display to which the pages are added
		 * @param ar the <code>Array</code> to which the operations are added
		 * @param doOpen an <code>Array</code> which contains the <code>INode</code>s which hold the pages to be opened
		 * @param all an <code>Array</code> which contains all <code>INode</code>s which hold the pages
		 * @param pageParams an <code>Array</code> which contains <code>PageParameter</code> instances  
		 */
		public function addInitializeAndShowOperations(display : Sprite, operations : Array, doOpen : Array, all : Array, pageParams : Array) : void {
			for each (var node : INode in all) {
				if(node.data is IPageInternal) {
					addInitializeAndShowOperation(display, operations, doOpen, node, IPageInternal(node.data), pageParams);
				}
			}
		}
		
		/**
		 * Adds the neccessary hiding operations for the current request.
		 * 
		 * @param display the display to which the pages are added
		 * @param ar the <code>Array</code> to which the operations are added
		 * @param doOpen an <code>Array</code> which contains the <code>INode</code>s which hold the pages to be opened
		 * @param all an <code>Array</code> which contains all <code>INode</code>s which hold the pages
		 */
		public function addHideOperations(operations : Array, doOpen : Array, all : Array) : void {
			var a : Array = new Array();
			for each (var node : INode in all) {
				if(node.data is IPageInternal) {
					addHideOperation(a, doOpen, node, IPageInternal(node.data));
				}
			}
			sortByOverlayAndLayer(operations, a);
		}
		
		/**
		 * Adds the neccessary finalization operations for the current request.
		 * 
		 * @param display the display to which the pages are added
		 * @param ar the <code>Array</code> to which the operations are added
		 * @param doOpen an <code>Array</code> which contains the <code>INode</code>s which hold the pages to be opened
		 * @param all an <code>Array</code> which contains all <code>INode</code>s which hold the pages
		 */
		public function addFinalizeOperations(operations : Array, doOpen : Array, all : Array) : void {
			var a : Array = new Array();
			for each (var node : INode in all) {
				if(node.data is IPageInternal) {
					addFinalizeOperation(a, doOpen, node, IPageInternal(node.data));
				}
			}
			sortByOverlayAndLayer(operations, a);
		};

		private static function sortByOverlayAndLayer(operations:Array, newOperations:Array) : void {
			newOperations = newOperations.sortOn(["isOverlay", "layer"], Array.DESCENDING | Array.NUMERIC);
			for each (var i : IQable in newOperations) {
				operations.push(i);
			}
		}

		public function addShowAndHideOperations(display : Sprite, operations : Array, doOpen : Array, all : Array) : void {
			var a : Array = new Array(); 
			for each (var node : INode in all) {
				if(node.data is IPageInternal) {
					a.concat(addShowHideOperation(display, a, doOpen, node, IPageInternal(node.data)));
				}
			}
			var q : IQ = new ParallelQ();
			for each (var op : IPageOperation in a) {
				q.add(op);
			}
			operations.push(new SortableOperationDecorator(q, node.id, node.layer, isOverlay(node)));
		}

		private function addFinalizeOperation(operations : Array, doOpen : Array, node : INode, page : IPageInternal) : void {
			if(!ArrayUtils.contains(doOpen, node)) {
				switch(page.state) {
					case PageState.INITIALIZING_STATE:
					case PageState.INITIALIZED_STATE:
					case PageState.HIDING_STATE:
					case PageState.HIDDEN_STATE:
					case PageState.SHOWING_STATE:
					case PageState.SHOWN_STATE:
						operations.push(new SortableOperationDecorator(new PageFinalizeOperation(page), page.id, node.layer, isOverlay(node)));
						break;
				}
			}
		}

		private function addHideOperation(operations : Array, doOpen : Array, node : INode, page : IPageInternal) : void {
			if(!ArrayUtils.contains(doOpen, node)) {
				switch(page.state) {
					case PageState.SHOWING_STATE:
					case PageState.SHOWN_STATE:
						operations.push(new SortableOperationDecorator(new PageHideOperation(page), page.id, node.layer, isOverlay(node)));
						break;
				}
			}
		}

		private function addInitializeOperation(display : Sprite, operations : Array, doOpen : Array, node : INode, page : IPageInternal, params : Array) : void {
			if(ArrayUtils.contains(doOpen, node)) {
				var isOverlay : Boolean = isOverlay(node);
				var q : IQ;
				switch(page.state) {
					case PageState.INSTANTIATED_STATE:
					case PageState.FINALIZED_STATE:
						q = new Q();
						q.add(new PageParamsOperation(page, params));
						q.add(new PageInitializeOperation(display, page));
						operations.push(new SortableOperationDecorator(q, page.id, node.layer, isOverlay));
						break;
					case PageState.INITIALIZING_STATE:
					case PageState.INITIALIZED_STATE:
						operations.push(new SortableOperationDecorator(new PageParamsOperation(page, params), page.id, node.layer, isOverlay));
						break;
					case PageState.SHOWING_STATE:
					case PageState.SHOWN_STATE:
						operations.push(new SortableOperationDecorator(new PageParamsOperation(page, params), page.id, node.layer, isOverlay));
						break;
					case PageState.HIDDEN_STATE:
					case PageState.HIDING_STATE:
						q = new Q();						q.add(new PageParamsOperation(page, params));
						operations.push(new SortableOperationDecorator(q, page.id, node.layer, isOverlay));
						break;
					case PageState.SHOWING_STATE:
					case PageState.SHOWN_STATE:
						operations.push(new SortableOperationDecorator(new PageParamsOperation(page, params), page.id, node.layer, isOverlay));
						break;
				}
			}
		}

		private function addShowOperation(display : Sprite, operations : Array, doOpen : Array, node : INode, page : IPageInternal) : void {
			if(ArrayUtils.contains(doOpen, node)) {
				
				var isOverlay : Boolean = isOverlay(node);
				switch(page.state) {
					case PageState.INSTANTIATED_STATE:
					case PageState.FINALIZED_STATE:
						operations.push(new SortableOperationDecorator(new PageShowOperation(display, page), page.id, node.layer, isOverlay));
						break;
					case PageState.HIDDEN_STATE:
					case PageState.HIDING_STATE:
						operations.push(new SortableOperationDecorator(new PageShowOperation(display, page), page.id, node.layer, isOverlay));
						break;
					case PageState.INITIALIZING_STATE:
					case PageState.INITIALIZED_STATE:
						operations.push(new SortableOperationDecorator(new PageShowOperation(display, page), page.id, node.layer, isOverlay));
						break;
				}
			}
		}

		private function addShowHideOperation(display : Sprite, operations : Array, doOpen : Array, node : INode, page : IPageInternal) : void {
			var q : IQ = new ParallelQ();
			
			var a : Array = new Array();			addShowOperation(display, a, doOpen, node, page);
			addHideOperation(a, doOpen, node, page);
			
			for each (var op : IPageOperation in a) {
				q.add(op);
			}
			
			operations.push(new SortableOperationDecorator(q, page.id, node.layer, isOverlay(node)));
		}

		
		private function addInitializeAndShowOperation(display : Sprite, operations : Array, doOpen : Array, node : INode, page : IPageInternal, params : Array) : void {
			if(ArrayUtils.contains(doOpen, node)) {
				
				var isOverlay : Boolean = isOverlay(node);
				var q : IQ;
				switch(page.state) {
					case PageState.INSTANTIATED_STATE:
					case PageState.FINALIZED_STATE:
						q = new Q();
						q.add(new PageParamsOperation(page, params));
						q.add(new PageInitializeOperation(display, page));
						q.add(new PageShowOperation(display, page));
						operations.push(new SortableOperationDecorator(q, page.id, node.layer, isOverlay));
						break;
					case PageState.HIDDEN_STATE:
					case PageState.HIDING_STATE:
						q = new Q();
						q.add(new PageParamsOperation(page, params));
						q.add(new PageShowOperation(display, page));
						operations.push(new SortableOperationDecorator(q, page.id, node.layer, isOverlay));
						break;
					case PageState.INITIALIZING_STATE:
					case PageState.INITIALIZED_STATE:
						q = new Q();
						q.add(new PageParamsOperation(page, params));
						q.add(new PageShowOperation(display, page));
						operations.push(new SortableOperationDecorator(q, page.id, node.layer, isOverlay));
						break;
					case PageState.SHOWING_STATE:
					case PageState.SHOWN_STATE:
						operations.push(new SortableOperationDecorator(new PageParamsOperation(page, params), page.id, node.layer, isOverlay));
						break;
				}
			}
		}
	}
}
